package com.lidroid.xutils.http;

import com.lidroid.xutils.hAdapter.AsyncTask;
import ohos.miscservices.timeutility.Time;
import com.lidroid.xutils.http.client.callback.RequestCallBackHandler;
import com.lidroid.xutils.http.client.callback.DefaultDownloadRedirectHandler;
import com.lidroid.xutils.http.client.callback.DownloadRedirectHandler;
import com.lidroid.xutils.http.client.callback.FileDownloadHandler;
import com.lidroid.xutils.http.client.callback.StringDownloadHandler;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.AbstractHttpClient;
import org.apache.http.protocol.HttpContext;

import java.io.File;
import java.io.IOException;
import java.net.UnknownHostException;


public class HttpHandler<T> extends AsyncTask<Object, Object, Object> implements RequestCallBackHandler {

    private int port = 443;

    private final AbstractHttpClient client;
    private final HttpContext context;

    private final StringDownloadHandler mStringDownloadHandler = new StringDownloadHandler();
    private final FileDownloadHandler mFileDownloadHandler = new FileDownloadHandler();

    private DownloadRedirectHandler downloadRedirectHandler;

    public void setDownloadRedirectHandler(DownloadRedirectHandler downloadRedirectHandler) {
        this.downloadRedirectHandler = downloadRedirectHandler;
    }

    private final RequestCallBack callback;

    private int executionCount = 0;
    private String targetUrl = null; //下载的路径
    private boolean isDownloadingFile;
    private boolean isResume = false; //是否断点续传
    private String charset;

    public HttpHandler(AbstractHttpClient client, HttpContext context, String charset, RequestCallBack callback) {
        this.client = client;
        this.context = context;
        this.callback = callback;
        this.charset = charset;
    }

    // 执行请求
    private void execRequestWithRetries(HttpRequestBase request) throws IOException {
        if (isResume && targetUrl != null) {
            File downloadFile = new File(targetUrl);
            long fileLen = 0;
            if (downloadFile.isFile() && downloadFile.exists()) {
                fileLen = downloadFile.length();
            }
            if (fileLen > 0)
                request.setHeader("RANGE", "bytes=" + fileLen + "-");
        }

        boolean retry = true;
        IOException ioException = null;
        HttpRequestRetryHandler retryHandler = client.getHttpRequestRetryHandler();
        while (retry) {
            try {
                if (!isCancelled()) {
                    client.getConnectionManager().getSchemeRegistry().register(
                            new Scheme("https", SSLSocketFactory.getSocketFactory(), port)
                    );
                    HttpResponse response = client.execute(request, context);
                    if (!isCancelled()) {
                        handleResponse(response);
                    }
                }
                return;
            } catch (UnknownHostException e) {
                Object[] obs = {UPDATE_FAILURE, e, "unknownHostException：can't resolve host"};
                publishProgress(obs);
                return;
            } catch (IOException e) {
                ioException = e;
                retry = retryHandler.retryRequest(ioException, ++executionCount, context);
            } catch (NullPointerException e) {
                ioException = new IOException("NPE in HttpClient" + e.getMessage());
                retry = retryHandler.retryRequest(ioException, ++executionCount, context);
            } catch (Exception e) {
                ioException = new IOException("Exception" + e.getMessage());
                retry = retryHandler.retryRequest(ioException, ++executionCount, context);
            }
        }
        if (ioException != null) {
            throw ioException;
        } else {
            throw new IOException("未知网络错误");
        }
    }

    @Override
    protected Object doInBackground(Object... params) {
        if (params != null && params.length == 3) {
            targetUrl = String.valueOf(params[1]);
            isDownloadingFile = targetUrl != null;
            isResume = (Boolean) params[2];
        }
        try {
            Object[] obs = {UPDATE_START};
            publishProgress(obs);
            execRequestWithRetries((HttpRequestBase) params[0]);
        } catch (IOException e) {
            Object[] obs = {UPDATE_FAILURE, e, e.getMessage()};
            publishProgress(obs);
        }

        return null;
    }

    private final static int UPDATE_START = 1;
    private final static int UPDATE_LOADING = 2;
    private final static int UPDATE_FAILURE = 3;
    private final static int UPDATE_SUCCESS = 4;

    @SuppressWarnings("unchecked")
    @Override
    protected void onProgressUpdate(Object... values) {
        int update = Integer.valueOf(String.valueOf(values[0]));
        switch (update) {
            case UPDATE_START:
                if (callback != null) {
                    callback.onStart();
                }
                break;
            case UPDATE_LOADING:
                if (callback != null) {
                    callback.onLoading(
                            Long.valueOf(String.valueOf(values[1])),
                            Long.valueOf(String.valueOf(values[2])));
                }
                break;
            case UPDATE_FAILURE:
                if (callback != null) {
                    callback.onFailure((Throwable) values[1], (String) values[2]);
                }
                break;
            case UPDATE_SUCCESS:
                if (callback != null) {
                    callback.onSuccess((T) values[1]);
                }
                break;
            default:
                break;
        }
        super.onProgressUpdate(values);
    }

    private void handleResponse(HttpResponse response) {
        StatusLine status = response.getStatusLine();
        if (status.getStatusCode() < 300) {
            try {
                HttpEntity entity = response.getEntity();
                Object responseBody = null;
                if (entity != null) {
                    lastUpdateTime = Time.getRealActiveTime();
                    if (isDownloadingFile) {
                        responseBody = mFileDownloadHandler.handleEntity(entity, this, targetUrl, isResume);
                    } else {
                        responseBody = mStringDownloadHandler.handleEntity(entity, this, charset);
                    }

                }
                Object[] obs = {UPDATE_SUCCESS, responseBody};
                publishProgress(obs);

            } catch (IOException e) {
                Object[] obs = {UPDATE_FAILURE, e, e.getMessage()};
                publishProgress(obs);
            }
        } else if (status.getStatusCode() == 302) {
            if (downloadRedirectHandler == null) {
                downloadRedirectHandler = new DefaultDownloadRedirectHandler();
            }
            HttpRequestBase request = downloadRedirectHandler.getDirectRequest(response);
            if (request != null) {
                try {
                    client.getConnectionManager().getSchemeRegistry().register(
                            new Scheme("https", SSLSocketFactory.getSocketFactory(), port)
                    );
                    response = client.execute(request, context);
                    if (!isCancelled()) {
                        handleResponse(response);
                    }
                } catch (IOException e) {
                    Object[] obs = {UPDATE_FAILURE, e, e.getMessage()};
                    publishProgress(obs);
                }
            }

        } else {
            String errorMsg = "response status error code:" + status.getStatusCode();
            if (status.getStatusCode() == 416 && isResume) {
                errorMsg += " \n maybe you have download complete.";
            }
            Object[] obs = {UPDATE_FAILURE,
                    new HttpResponseException(status.getStatusCode(), status.getReasonPhrase()),
                    errorMsg};
            publishProgress(obs);
        }
    }

    private boolean mStop = false;

    /**
     * 停止下载任务
     */
    @Override
    public void stop() {
        this.mStop = true;
    }

    public boolean isStop() {
        return mStop;
    }

    private long lastUpdateTime;

    @Override
    public boolean updateProgress(long total, long current, boolean forceUpdateUI) {
        if (mStop) {
            return !mStop;
        }
        if (callback != null && callback.isProgress()) {
            if (forceUpdateUI) {
                Object[] obs = {UPDATE_LOADING, total, current};
                publishProgress(obs);
            } else {
                long currTime = Time.getRealActiveTime();
                if (currTime - lastUpdateTime >= callback.getRate()) {
                    lastUpdateTime = currTime;
                    Object[] obs = {UPDATE_LOADING, total, current};
                    publishProgress(obs);
                }
            }
        }
        return !mStop;
    }

}
